/**
 * @brief 测试几种遍历mat像素方法的效率
 * @see https://docs.opencv.org/4.x/db/da5/tutorial_how_to_scan_images.html
 */
#include <iostream>
#include <opencv2/core/utility.hpp>
#include <opencv2/opencv.hpp>

int main(void) {
    using cv::getTickCount;
    using cv::getTickFrequency;
    using cv::Mat;

    using std::cout;
    using std::endl;

    unsigned char table[256];

    for (int i = 0; i < 256; ++i) {
        table[i] = i / 10 * 10;
    }

    Mat src = imread("test.jpg", cv::IMREAD_GRAYSCALE);
    Mat bak;
    resize(src, bak, cv::Size(), 0.25, 0.25);

    int64_t timer;
    double  result;

    src          = bak.clone();
    int channels = src.channels();
    int nRows    = src.rows;
    int nCols    = src.cols * channels;

    if (src.isContinuous()) {
        nCols *= nRows;
        nRows = 1;
    }
    cout << "rows: " << nRows << " cols:" << nCols << '\n';

    timer = cv::getTickCount();

    uchar* ptr = src.data;
    for (int i = 0; i < nRows; ++i) {
        for (int j = 0; j < nCols; ++j) {
            ptr[j] = table[ptr[j]];
        }
    }

    result = (getTickCount() - timer) / getTickFrequency();
    cout << "efficient way: " << result * 1e6 << "us\n";
    imwrite("result.jpg", src);

    src   = bak.clone();
    timer = getTickCount();

    cv::MatIterator_<uchar> iter;
    cv::MatIterator_<uchar> end;
    for (iter = src.begin<uchar>(), end = src.end<uchar>(); iter != end; ++iter) {
        *iter = table[*iter];
    }

    result = (getTickCount() - timer) / getTickFrequency();
    cout << "iterator way: " << result * 1e6 << "us\n";

    src   = bak.clone();
    timer = getTickCount();

    for (int i = 0; i < src.rows; ++i) {
        for (int j = 0; j < src.cols; ++j) {
            src.at<uchar>(i, j) = table[src.at<uchar>(i, j)];
        }
    }

    result = (getTickCount() - timer) / getTickFrequency();
    cout << "row by row way: " << result * 1e6 << "us\n";

    src   = bak.clone();
    timer = getTickCount();

    for (int i = 0; i < src.cols; ++i) {
        for (int j = 0; j < src.rows; ++j) {
            src.at<uchar>(j, i) = table[src.at<uchar>(j, i)];
        }
    }

    result = (getTickCount() - timer) / getTickFrequency();
    cout << "col by col way: " << result * 1e6 << "us\n";

    src   = bak.clone();
    timer = getTickCount();

    for (int i = 0; i < src.rows; ++i) {
        auto* ptr = src.ptr<uchar>(i);
        for (int j = 0; j < src.cols; ++j) {
            ptr[j] = table[ptr[j]];
        }
    }

    result = (getTickCount() - timer) / getTickFrequency();
    cout << "row by row with ptr way: " << result * 1e6 << "us\n";

    src      = bak.clone();
    Mat mask = Mat(1, 256, CV_8U, table);
    Mat out(src.size(), CV_8U);
    timer = getTickCount();

    LUT(src, mask, out);

    result = (getTickCount() - timer) / getTickFrequency();
    cout << "LUT way: " << result * 1e6 << "us\n";
}
/**
rows: 1 cols:810000
efficient way: 785.443us
iterator way: 3572.33us
row by row way: 2398.4us
col by col way: 3234.64us
row by row with ptr way: 756.202us
LUT way: 191.146us
*/
